Last Updated: December 19, 2025
A Movie Ticket Booking System is a software application that enables users to search for movies, view showtimes, select seats, and book tickets at cinemas or multiplexes.
Book your movie experience
In this chapter, we will explore the low-level design of a movie ticket booking system in detail.
Let's start by clarifying the requirements:
Before starting the design, it's important to ask thoughtful questions to uncover hidden assumptions and better define the scope of the system.
Here is an example of how a conversation between the candidate and the interviewer might unfold:
Candidate: Is this system for the end-user (customer) to book tickets, or is it an internal system for cinema administrators to manage shows and screens?
Interviewer: Let's focus on the customer-facing booking workflow. The primary user story is a customer finding a movie and booking seats for a specific show.
Candidate: How should users find movies? Should they be able to search by movie title, city, or cinema?
Interviewer: Users should be able to find all shows for a specific movie in a given city.
Candidate: Should the system support different seat types such as ‘Standard’, ‘Premium’, and ‘Recliner’, with variable pricing?
Interviewer: Yes, each screen can have multiple seat types with different pricing.
Candidate: Should users be able to select specific seats, or should the system assign them automatically?
Interviewer: Users must be able to select specific seats from a seat map during the booking process.
Candidate: A critical issue in booking systems is concurrency. What should happen if two users try to book the same seat at the same time?
Interviewer: Excellent point. The system must prevent double-booking. When a user selects seats, those seats should be temporarily locked for a short duration while they complete the payment. If the payment is not completed within the timeout, the seats should be released.
Candidate: How is payment handled? Do we need to integrate with a real payment gateway?
Interviewer: We can assume an external payment gateway. Our system should be able to initiate a payment process with a specific amount and handle success or failure responses. The design should allow for different payment methods, like credit cards or PayPal.
Candidate: Should there be a notification feature? For example, notifying users when booking opens for a new, highly anticipated movie.
Interviewer: Yes, that's a great feature to include. Let's add a mechanism for users to subscribe to a movie and receive a notification when it becomes available for booking.
After gathering the details, we can summarize the key system requirements.
After the requirements are clear, lets identify the core entities/objects we will have in our system.
Core entities are the fundamental building blocks of our system. We identify them by analyzing key nouns (e.g., movie, theater, screen, seat, user, booking) and actions (e.g., list, search, book, cancel, lock) from the functional requirements. These typically translate into classes, enums, or interfaces in an object-oriented design.
Let’s walk through the functional requirements and extract the relevant entities:
This establishes the hierarchical structure of the system's catalog.
City: The top-level geographical entity.Cinema: A physical location within a City that contains multiple screens.Screen: A specific auditorium within a Cinema.Seat: The smallest unit within a Screen. It has properties like SeatType (REGULAR, PREMIUM) and SeatStatus (AVAILABLE, BOOKED, LOCKED), which are managed by enums.Movie: Represents the content being shown.Show: The central entity that connects a Movie to a Screen at a specific time. This is what a user ultimately books.This introduces the core actors and the outcome of the booking process.
User: Represents the customer making the booking.Booking: An entity that represents a confirmed transaction, linking a User, a Show, and a specific list of Seats.This critical concurrency requirement necessitates a dedicated manager for handling seat states.
SeatLockManager: A service entity responsible for atomically locking and unlocking seats for a user during the payment process. It prevents race conditions where two users might try to book the same seat simultaneously.These requirements for flexible, interchangeable algorithms are ideal for the Strategy Pattern.
PricingStrategy (Interface): Defines a contract for different pricing models (e.g., WeekdayPricingStrategy, WeekendPricingStrategy). A Show is configured with a specific strategy.PaymentStrategy (Interface): Defines a contract for various payment gateways (e.g., CreditCardPaymentStrategy).Payment: An entity to record the details of the financial transaction.To manage the orchestration of locking, pricing, payment, and booking confirmation, two key service entities are introduced.
BookingManager: Orchestrates the step-by-step workflow of creating a single booking.MovieBookingService: Acts as a Facade and Singleton, providing a unified and simplified entry point for all client interactions, hiding the complexity of the underlying services and data models.These core entities define the key abstractions of the Movie Ticket Booking System and will guide the structure of your low-level design and class diagrams.
This section breaks down the system's architecture into its fundamental classes, their responsibilities, and the relationships that connect them. We also explore the key design patterns that provide robustness and flexibility to the solution.
The system is composed of several types of classes, each with a distinct role.
PaymentStatus: Defines the outcome of a payment transaction (SUCCESS, FAILURE, PENDING).SeatStatus: Represents the state of a seat (AVAILABLE, BOOKED, LOCKED). The LOCKED state is crucial for handling concurrency during the booking process.SeatType: Classifies seats and associates a base price with each type (REGULAR, PREMIUM, RECLINER).UserRepresents a customer of the booking system.
CityRepresents a geographical city where cinemas are located.
MovieRepresents a film, containing its title and duration. It also acts as a Subject in the Observer pattern.
Seat
Represents a single seat in a cinema screen, with properties like row, column, type, and status.
ScreenA container for a collection of Seats.
CinemaRepresents a physical movie theater, containing its name, city, and a list of its Screens.
PaymentA data object that records the details of a completed payment transaction.
Booking
A data class that encapsulates all details of a confirmed booking, including the user, show, seats, and payment information.
It is constructed using the Builder pattern.
ShowRepresents a specific screening of a Movie at a particular Screen and startTime. It is associated with a PricingStrategy to determine ticket costs.
SeatLockManagerA critical service responsible for handling concurrency.
It temporarily locks seats for a user during the booking process to prevent double-booking, and automatically releases them after a timeout.
BookingManagerAn orchestrator class that manages the entire booking workflow.
It uses the SeatLockManager and a PaymentStrategy to ensure a booking is processed atomically and reliably.
MovieBookingService (Singleton & Facade)The main entry point for the application.
It hides the system's internal complexity from the client and provides a simple, unified API for all major operations like searching for shows and booking tickets.
The relationships between classes define the system's structure and data flow.
Cinema is composed of one or more Screens.Screen is composed of a collection of Seats.MovieBookingService "has-a" collection of all core entities like Cinemas, Movies, and Shows, managing their lifecycle.Show is associated with one Movie, one Screen, and one PricingStrategy.Booking is associated with one User, one Show, and a list of Seats.MovieSubject (the Movie) is associated with a list of MovieObservers.Movie extends the abstract MovieSubject class.UserObserver implements the MovieObserver interface.WeekdayPricingStrategy, CreditCardPaymentStrategy, etc.) implement their respective PricingStrategy and PaymentStrategy interfaces.MovieBookingService (Facade) depends on the BookingManager to handle the booking process.BookingManager depends on the SeatLockManager to handle seat locking and on a PaymentStrategy to process payments.Booking.BookingBuilder to construct a Booking object.This pattern is used to make core algorithms interchangeable.
The PricingStrategy allows different pricing models (e.g., weekday vs. weekend) to be applied to a Show without changing the Show or BookingManager classes.
The PaymentStrategy allows different payment methods (e.g., Credit Card, UPI) to be used for a booking.
This pattern is used to notify users about movie updates.
The Movie (Subject) can notify all subscribed UserObservers when an event occurs (e.g., bookings open), decoupling the movie entity from the user notification logic.
The Booking.BookingBuilder is used for the step-by-step construction of a Booking object. This is ideal for an object with multiple required fields, ensuring it is created in a valid and consistent state.
The MovieBookingService class serves as a facade. It provides a simple, high-level API (findShows, bookTickets) that hides the complex internal workflows involving seat locking, payment processing, and data management.
MovieBookingService is implemented as a singleton to ensure there is a single, globally accessible point of control for the entire booking system. This centralizes the management of all data and services.
These enums standardize constants used across the system
1class PaymentStatus(Enum):
2 SUCCESS = "SUCCESS"
3 FAILURE = "FAILURE"
4 PENDING = "PENDING"
5
6
7class SeatStatus(Enum):
8 AVAILABLE = "AVAILABLE"
9 BOOKED = "BOOKED"
10 LOCKED = "LOCKED" # Temporarily held during booking process
11
12
13class SeatType(Enum):
14 REGULAR = 50.0
15 PREMIUM = 80.0
16 RECLINER = 120.0
17
18 def get_price(self) -> float:
19 return self.valueSeatType maps each seat type to a price.SeatStatus enables clear transitions during the booking process.PaymentStatus helps track payment outcomes.1class User:
2 def __init__(self, name: str, email: str):
3 self.id = str(uuid.uuid4())
4 self.name = name
5 self.email = email
6
7 def get_id(self) -> str:
8 return self.id
9
10 def get_name(self) -> str:
11 return self.name1class City:
2 def __init__(self, city_id: str, name: str):
3 self.id = city_id
4 self.name = name
5
6 def get_id(self) -> str:
7 return self.id
8
9 def get_name(self) -> str:
10 return self.name1class Movie(MovieSubject):
2 def __init__(self, movie_id: str, title: str, duration_in_minutes: int):
3 super().__init__()
4 self.id = movie_id
5 self.title = title
6 self.duration_in_minutes = duration_in_minutes
7
8 def get_id(self) -> str:
9 return self.id
10
11 def get_title(self) -> str:
12 return self.title1class Seat:
2 def __init__(self, seat_id: str, row: int, col: int, seat_type: SeatType):
3 self.id = seat_id
4 self.row = row
5 self.col = col
6 self.type = seat_type
7 self.status = SeatStatus.AVAILABLE
8
9 def get_id(self) -> str:
10 return self.id
11
12 def get_row(self) -> int:
13 return self.row
14
15 def get_col(self) -> int:
16 return self.col
17
18 def get_type(self) -> SeatType:
19 return self.type
20
21 def get_status(self) -> SeatStatus:
22 return self.status
23
24 def set_status(self, status: SeatStatus) -> None:
25 self.status = status1class Screen:
2 def __init__(self, screen_id: str):
3 self.id = screen_id
4 self.seats: List[Seat] = []
5
6 def add_seat(self, seat: Seat) -> None:
7 self.seats.append(seat)
8
9 def get_id(self) -> str:
10 return self.id
11
12 def get_seats(self) -> List[Seat]:
13 return self.seats1class Cinema:
2 def __init__(self, cinema_id: str, name: str, city: City, screens: List[Screen]):
3 self.id = cinema_id
4 self.name = name
5 self.city = city
6 self.screens = screens
7
8 def get_id(self) -> str:
9 return self.id
10
11 def get_name(self) -> str:
12 return self.name
13
14 def get_city(self) -> City:
15 return self.city
16
17 def get_screens(self) -> List[Screen]:
18 return self.screens1class Show:
2 def __init__(self, show_id: str, movie: Movie, screen: Screen, start_time: datetime, pricing_strategy: PricingStrategy):
3 self.id = show_id
4 self.movie = movie
5 self.screen = screen
6 self.start_time = start_time
7 self.pricing_strategy = pricing_strategy
8
9 def get_id(self) -> str:
10 return self.id
11
12 def get_movie(self) -> Movie:
13 return self.movie
14
15 def get_screen(self) -> Screen:
16 return self.screen
17
18 def get_start_time(self) -> datetime:
19 return self.start_time
20
21 def get_pricing_strategy(self) -> PricingStrategy:
22 return self.pricing_strategy1class Payment:
2 def __init__(self, amount: float, status: PaymentStatus, transaction_id: str):
3 self.id = str(uuid.uuid4())
4 self.amount = amount
5 self.status = status
6 self.transaction_id = transaction_id
7
8 def get_status(self) -> PaymentStatus:
9 return self.status1class Booking:
2 def __init__(self, booking_id: str, user: User, show: Show, seats: List[Seat], total_amount: float, payment: Payment):
3 self.id = booking_id
4 self.user = user
5 self.show = show
6 self.seats = seats
7 self.total_amount = total_amount
8 self.payment = payment
9
10 def confirm_booking(self) -> None:
11 """Marks seats as BOOKED upon successful booking creation"""
12 for seat in self.seats:
13 seat.set_status(SeatStatus.BOOKED)
14
15 def get_id(self) -> str:
16 return self.id
17
18 def get_user(self) -> User:
19 return self.user
20
21 def get_show(self) -> Show:
22 return self.show
23
24 def get_seats(self) -> List[Seat]:
25 return self.seats
26
27 def get_total_amount(self) -> float:
28 return self.total_amount
29
30 def get_payment(self) -> Payment:
31 return self.payment
32
33 class BookingBuilder:
34 def __init__(self):
35 self.id: Optional[str] = None
36 self.user: Optional[User] = None
37 self.show: Optional[Show] = None
38 self.seats: Optional[List[Seat]] = None
39 self.total_amount: Optional[float] = None
40 self.payment: Optional[Payment] = None
41
42 def set_id(self, booking_id: str) -> 'Booking.BookingBuilder':
43 self.id = booking_id
44 return self
45
46 def set_user(self, user: User) -> 'Booking.BookingBuilder':
47 self.user = user
48 return self
49
50 def set_show(self, show: Show) -> 'Booking.BookingBuilder':
51 self.show = show
52 return self
53
54 def set_seats(self, seats: List[Seat]) -> 'Booking.BookingBuilder':
55 self.seats = seats
56 return self
57
58 def set_total_amount(self, total_amount: float) -> 'Booking.BookingBuilder':
59 self.total_amount = total_amount
60 return self
61
62 def set_payment(self, payment: Payment) -> 'Booking.BookingBuilder':
63 self.payment = payment
64 return self
65
66 def build(self) -> 'Booking':
67 # Validations can be added here
68 return Booking(self.id, self.user, self.show, self.seats, self.total_amount, self.payment)This pattern allows users to subscribe to movie updates, such as when booking opens. The Movie (Subject) is completely decoupled from the User (Observer). The Movie doesn't know what a User is; it only knows it has a list of MovieObservers to notify.
1class MovieObserver(ABC):
2 @abstractmethod
3 def update(self, movie: 'Movie') -> None:
4 pass
5
6class MovieSubject:
7 def __init__(self):
8 self.observers: List[MovieObserver] = []
9
10 def add_observer(self, observer: MovieObserver) -> None:
11 self.observers.append(observer)
12
13 def remove_observer(self, observer: MovieObserver) -> None:
14 if observer in self.observers:
15 self.observers.remove(observer)
16
17 def notify_observers(self) -> None:
18 for observer in self.observers:
19 observer.update(self)
20
21class UserObserver(MovieObserver):
22 def __init__(self, user: User):
23 self.user = user
24
25 def update(self, movie: Movie) -> None:
26 print(f"Notification for {self.user.get_name()} ({self.user.get_id()}): Movie '{movie.get_title()}' is now available for booking!")The Strategy pattern is used to define a family of algorithms, encapsulate each one, and make them interchangeable. This is perfect for pricing and payment, which can have multiple variations.
1class PricingStrategy(ABC):
2 @abstractmethod
3 def calculate_price(self, seats: List[Seat]) -> float:
4 pass
5
6class WeekdayPricingStrategy(PricingStrategy):
7 def calculate_price(self, seats: List[Seat]) -> float:
8 return sum(seat.get_type().get_price() for seat in seats)
9
10class WeekendPricingStrategy(PricingStrategy):
11 WEEKEND_SURCHARGE = 1.2 # 20% surcharge
12
13 def calculate_price(self, seats: List[Seat]) -> float:
14 base_price = sum(seat.get_type().get_price() for seat in seats)
15 return base_price * self.WEEKEND_SURCHARGEThis pattern decouples the context (a Show or a BookingManager) from the concrete implementation of the algorithm. The Show doesn't need to know how to calculate a weekend price; it just calls calculatePrice() on its configured strategy object.
1class PaymentStrategy(ABC):
2 @abstractmethod
3 def pay(self, amount: float) -> Payment:
4 pass
5
6
7class CreditCardPaymentStrategy(PaymentStrategy):
8 def __init__(self, card_number: str, cvv: str):
9 self.card_number = card_number
10 self.cvv = cvv
11
12 def pay(self, amount: float) -> Payment:
13 print(f"Processing credit card payment of ${amount:.2f}")
14 # Simulate payment gateway interaction
15 payment_success = random.random() > 0.05 # 95% success rate
16 return Payment(
17 amount,
18 PaymentStatus.SUCCESS if payment_success else PaymentStatus.FAILURE,
19 f"TXN_{uuid.uuid4()}"
20 )1class SeatLockManager:
2 def __init__(self):
3 self.locked_seats: Dict[Show, Dict[Seat, str]] = {}
4 self.executor = ThreadPoolExecutor(max_workers=5)
5 self.LOCK_TIMEOUT_SECONDS = 0.5 # 0.5 seconds. In real world, timeout would be in minutes
6
7 def lock_seats(self, show: Show, seats: List[Seat], user_id: str) -> None:
8 # Use a lock per show to ensure atomicity for that specific show
9 show_lock = getattr(show, '_lock', None)
10 if show_lock is None:
11 show._lock = threading.Lock()
12 show_lock = show._lock
13
14 with show_lock:
15 # Check if any of the requested seats are already locked or booked
16 for seat in seats:
17 if seat.get_status() != SeatStatus.AVAILABLE:
18 print(f"Seat {seat.get_id()} is not available.")
19 return
20
21 # Lock the seats
22 for seat in seats:
23 seat.set_status(SeatStatus.LOCKED)
24
25 if show not in self.locked_seats:
26 self.locked_seats[show] = {}
27
28 for seat in seats:
29 self.locked_seats[show][seat] = user_id
30
31 # Schedule a task to unlock the seats after a timeout
32 self.executor.submit(self._unlock_after_timeout, show, seats, user_id)
33 print(f"Locked seats: {[seat.get_id() for seat in seats]} for user {user_id}")
34
35 def _unlock_after_timeout(self, show: Show, seats: List[Seat], user_id: str) -> None:
36 time.sleep(self.LOCK_TIMEOUT_SECONDS)
37 self.unlock_seats(show, seats, user_id)
38
39 def unlock_seats(self, show: Show, seats: List[Seat], user_id: str) -> None:
40 show_lock = getattr(show, '_lock', None)
41 if show_lock is None:
42 return
43
44 with show_lock:
45 show_locks = self.locked_seats.get(show)
46 if show_locks is not None:
47 for seat in seats:
48 # Only unlock if it's still locked by the same user (prevents race conditions)
49 if seat in show_locks and show_locks[seat] == user_id:
50 del show_locks[seat]
51 if seat.get_status() == SeatStatus.LOCKED:
52 seat.set_status(SeatStatus.AVAILABLE)
53 print(f"Unlocked seat: {seat.get_id()} due to timeout.")
54 else:
55 print(f"Unlocked seat: {seat.get_id()} due to booking completion.")
56
57 if not show_locks:
58 del self.locked_seats[show]
59
60 def shutdown(self) -> None:
61 print("Shutting down SeatLockProvider scheduler.")
62 self.executor.shutdown(wait=True)The BookingManager orchestrates the entire booking workflow.
1class BookingManager:
2 def __init__(self, seat_lock_manager: SeatLockManager):
3 self.seat_lock_manager = seat_lock_manager
4
5 def create_booking(self, user: User, show: Show, seats: List[Seat], payment_strategy: PaymentStrategy) -> Optional[Booking]:
6 # 1. Lock the seats
7 self.seat_lock_manager.lock_seats(show, seats, user.get_id())
8
9 # 2. Calculate the total price
10 total_amount = show.get_pricing_strategy().calculate_price(seats)
11
12 # 3. Process Payment
13 payment = payment_strategy.pay(total_amount)
14
15 # 4. If payment is successful, create the booking
16 if payment.get_status() == PaymentStatus.SUCCESS:
17 booking = Booking.BookingBuilder() \
18 .set_user(user) \
19 .set_show(show) \
20 .set_seats(seats) \
21 .set_total_amount(total_amount) \
22 .set_payment(payment) \
23 .build()
24
25 # 5. Confirm the booking (mark seats as BOOKED)
26 booking.confirm_booking()
27
28 # Clean up the lock map
29 self.seat_lock_manager.unlock_seats(show, seats, user.get_id())
30
31 return booking
32 else:
33 print("Payment failed. Please try again.")
34 return NoneBookingManager defines the strict, sequential workflow for a booking: lock -> calculate price -> process payment -> create booking -> confirm. This ensures a consistent and reliable process.
This class is a Singleton that provides a simplified, high-level API for clients.
1class MovieBookingService:
2 _instance: Optional['MovieBookingService'] = None
3 _lock = threading.Lock()
4
5 def __new__(cls):
6 if cls._instance is None:
7 with cls._lock:
8 if cls._instance is None:
9 cls._instance = super().__new__(cls)
10 return cls._instance
11
12 def __init__(self):
13 if hasattr(self, 'initialized'):
14 return
15
16 self.cities: Dict[str, City] = {}
17 self.cinemas: Dict[str, Cinema] = {}
18 self.movies: Dict[str, Movie] = {}
19 self.users: Dict[str, User] = {}
20 self.shows: Dict[str, Show] = {}
21
22 self.seat_lock_manager = SeatLockManager()
23 self.booking_manager = BookingManager(self.seat_lock_manager)
24 self.initialized = True
25
26 @classmethod
27 def get_instance(cls) -> 'MovieBookingService':
28 return cls()
29
30 def get_booking_manager(self) -> BookingManager:
31 return self.booking_manager
32
33 # Data Management Methods
34 def add_city(self, city_id: str, name: str) -> City:
35 city = City(city_id, name)
36 self.cities[city.get_id()] = city
37 return city
38
39 def add_cinema(self, cinema_id: str, name: str, city_id: str, screens: List[Screen]) -> Cinema:
40 city = self.cities[city_id]
41 cinema = Cinema(cinema_id, name, city, screens)
42 self.cinemas[cinema.get_id()] = cinema
43 return cinema
44
45 def add_movie(self, movie: Movie) -> None:
46 self.movies[movie.get_id()] = movie
47
48 def add_show(self, show_id: str, movie: Movie, screen: Screen, start_time: datetime, pricing_strategy: PricingStrategy) -> Show:
49 show = Show(show_id, movie, screen, start_time, pricing_strategy)
50 self.shows[show.get_id()] = show
51 return show
52
53 def create_user(self, name: str, email: str) -> User:
54 user = User(name, email)
55 self.users[user.get_id()] = user
56 return user
57
58 def book_tickets(self, user_id: str, show_id: str, desired_seats: List[Seat], payment_strategy: PaymentStrategy) -> Optional[Booking]:
59 return self.booking_manager.create_booking(
60 self.users[user_id],
61 self.shows[show_id],
62 desired_seats,
63 payment_strategy
64 )
65
66 # Search Functionality
67 def find_shows(self, movie_title: str, city_name: str) -> List[Show]:
68 result = []
69 for show in self.shows.values():
70 if show.get_movie().get_title().lower() == movie_title.lower():
71 cinema = self._find_cinema_for_show(show)
72 if cinema and cinema.get_city().get_name().lower() == city_name.lower():
73 result.append(show)
74 return result
75
76 def _find_cinema_for_show(self, show: Show) -> Optional[Cinema]:
77 # This is inefficient. In a real system, shows would have a direct link to the cinema.
78 # For this example, we traverse the cinema list.
79 for cinema in self.cinemas.values():
80 if show.get_screen() in cinema.get_screens():
81 return cinema
82 return None
83
84 def shutdown(self) -> None:
85 self.seat_lock_manager.shutdown()
86 print("MovieTicketBookingSystem has been shut down.")The demo class validates the entire system by simulating a user's booking journey.
1class MovieBookingDemo:
2 @staticmethod
3 def main():
4 # Setup
5 service = MovieBookingService.get_instance()
6
7 nyc = service.add_city("city1", "New York")
8 la = service.add_city("city2", "Los Angeles")
9
10 # 2. Add movies
11 matrix = Movie("M1", "The Matrix", 120)
12 avengers = Movie("M2", "Avengers: Endgame", 170)
13 service.add_movie(matrix)
14 service.add_movie(avengers)
15
16 # Add Seats for a Screen
17 screen1 = Screen("S1")
18
19 for i in range(1, 11):
20 seat_type = SeatType.REGULAR if i <= 5 else SeatType.PREMIUM
21 screen1.add_seat(Seat(f"A{i}", 1, i, seat_type))
22 screen1.add_seat(Seat(f"B{i}", 2, i, seat_type))
23
24 # Add Cinemas
25 amc_nyc = service.add_cinema("cinema1", "AMC Times Square", nyc.get_id(), [screen1])
26
27 # Add Shows
28 matrix_show = service.add_show("show1", matrix, screen1, datetime.now() + timedelta(hours=2), WeekdayPricingStrategy())
29 avengers_show = service.add_show("show2", avengers, screen1, datetime.now() + timedelta(hours=5), WeekdayPricingStrategy())
30
31 # User and Observer Setup
32 alice = service.create_user("Alice", "[email protected]")
33 alice_observer = UserObserver(alice)
34 avengers.add_observer(alice_observer)
35
36 # Simulate movie release
37 print("\n--- Notifying Observers about Movie Release ---")
38 avengers.notify_observers()
39
40 # User Story: Alice books tickets
41 print("\n--- Alice's Booking Flow ---")
42 city_name = "New York"
43 movie_title = "Avengers: Endgame"
44
45 # 1. Search for shows
46 available_shows = service.find_shows(movie_title, city_name)
47 if not available_shows:
48 print(f"No shows found for {movie_title} in {city_name}")
49 return
50
51 selected_show = available_shows[0] # Alice selects the first show
52
53 # 2. View available seats
54 available_seats = [seat for seat in selected_show.get_screen().get_seats() if seat.get_status() == SeatStatus.AVAILABLE]
55 print(f"Available seats for '{selected_show.get_movie().get_title()}' at {selected_show.get_start_time()}: {[seat.get_id() for seat in available_seats]}")
56
57 # 3. Select seats
58 desired_seats = [available_seats[2], available_seats[3]]
59 print(f"Alice selects seats: {[seat.get_id() for seat in desired_seats]}")
60
61 # 4. Book Tickets
62 booking = service.book_tickets(
63 alice.get_id(),
64 selected_show.get_id(),
65 desired_seats,
66 CreditCardPaymentStrategy("1234-5678-9876-5432", "123")
67 )
68
69 if booking:
70 print("\n--- Booking Successful! ---")
71 print(f"Booking ID: {booking.get_id()}")
72 print(f"User: {booking.get_user().get_name()}")
73 print(f"Movie: {booking.get_show().get_movie().get_title()}")
74 print(f"Seats: {[seat.get_id() for seat in booking.get_seats()]}")
75 print(f"Total Amount: ${booking.get_total_amount()}")
76 print(f"Payment Status: {booking.get_payment().get_status().value}")
77 else:
78 print("Booking failed.")
79
80 # 5. Verify seat status after booking
81 print("\nSeat status after Alice's booking:")
82 for seat in desired_seats:
83 print(f"Seat {seat.get_id()} status: {seat.get_status().value}")
84
85 # 6. Shut down the system to release resources like the scheduler.
86 service.shutdown()
87
88
89if __name__ == "__main__":
90 MovieBookingDemo.main()Which entity forms the link between a Movie, a Screen, and a specific time in a Movie Booking System?
I see one issue in this system is that you are maintaining seatStatus inside seat which seems to be incorrect as same seat can be used for two different shows, so may be its better to maintain a data structure inside the shows which stores the list of bookedSeats,
because in your current approach if you change the seatStatus then other shows will not be able to see the seats as well